package com.gss.plugin.monitor.collect;

import com.gss.plugin.monitor.entity.ApiInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

/**
 * @author: DYang
 * @since: 2023/8/17
 * @description: 使用aop的方式进行采集接口
 */
@Slf4j
@Aspect
@Component
public class ApiAopMonitor {


    private final HttpServletRequest request;

    private long startTime;

    @Autowired
    public ApiAopMonitor(HttpServletRequest request) {
        this.request = request;
    }

    @Before("@annotation(io.swagger.annotations.ApiOperation)")
    public void logBefore(JoinPoint joinPoint) {
        //请求开始时间
        request.setAttribute("request_start_time",System.currentTimeMillis());
    }

    @AfterReturning(pointcut = "@annotation(io.swagger.annotations.ApiOperation)", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        // 记录日志
        this.recordApiLog(joinPoint,result);
    }

    /**
     * 记录切点日志
     * @param joinPoint
     * @param result
     */
    private void recordApiLog(JoinPoint joinPoint, Object result) {
        try {
            ApiInfo info = new ApiInfo();
            //获取方法
            Method method = this.getMethod(joinPoint);
            if (method != null && method.isAnnotationPresent(ApiOperation.class)){
                ApiOperation methodAnno = method.getAnnotation(ApiOperation.class);
                //获取当前method 所在的类
                Class<?> aClass = method.getDeclaringClass();
                Api apiAnno = aClass.getAnnotation(Api.class);
                //接口名称
                String methodName = method.getDeclaringClass().getName() + "." +  method.getName();
                info.setApiMethod(methodName);
                info.setApiName(methodAnno.value());
                //模块名称
                if (apiAnno != null) {
                    info.setModuleName(Arrays.toString(apiAnno.tags()));
                }
                //接口类型
                info.setMethodType(methodAnno.httpMethod());
                //响应状态码
                info.setResponseCode(methodAnno.code());
            }
            //请求url
            info.setUrl(this.getRequestUrl());
            //请求ip
            info.setRequestIp(this.getClientIP());
            //请求参数
            info.setParams(Arrays.toString(joinPoint.getArgs()));
            //响应时间
            Object obj = request.getAttribute("request_start_time");
            if (obj != null) {
                long startTime = (long) obj;
                //请求时间
                Date date = new Date(startTime);
                info.setRequestTime(date);
                //响应时间
                long responseTime = System.currentTimeMillis() - startTime;
                info.setResponseTime(responseTime);

            }
            //响应结果
            info.setResponse(result != null ? (String) result : null);
            //请求用户id
            String userId = request.getHeader("userId");
            info.setUserId(userId != null ? Integer.valueOf(userId) : null);

            log.info("【切点日志】 接口请求信息 {}",info);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("【接口切点】 记录接口日志异常 ",e);
        }
    }

    private String getRequestUrl() {
        return request.getRequestURL().toString();
    }

    private Method getMethod(JoinPoint joinPoint) {
        // Get the method signature from the join point
        String methodName = joinPoint.getSignature().getName();
        Class<?> targetClass = joinPoint.getTarget().getClass();

        // Iterate through the methods of the target class to find the method with the matching name
        for (Method method : targetClass.getDeclaredMethods()) {
            if (method.getName().equals(methodName)) {
                return method;
            }
        }
        return null;
    }

    private String getClientIP() {
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        return ipAddress;
    }

}
